﻿using System;
using System.ComponentModel;
using System.Windows.Input;
using BOL.Apps.GeneNetworkBrowser.ViewModels;

namespace BOL.Apps.GeneNetworkBrowser.Analysis
{
    public sealed class WorkItem<I, O> : BackgroundWorker, INotifyPropertyChanged
    {
        #region Private variables

        private I _args;
        private O _result;
        private WorkStatus _status;
        private double? _progress;
        private RelayCommand _cancelCommand;

        #endregion

        #region Public properties

        public I Args
        {
            get { return _args; }
            set
            {
                if (!value.Equals(_args))
                {
                    _args = value;
                    OnPropertyChanged("Args");
                }
            }
        }

        public WorkStatus Status
        {
            get { return _status; }
            set
            {
                if (_status != value)
                {
                    _status = value;
                    OnPropertyChanged("Status");
                    CommandManager.InvalidateRequerySuggested();
                }
            }
        }

        public double? Progress
        {
            get { return _progress; }
            set
            {
                if (_progress != value)
                {
                    _progress = value;
                    OnPropertyChanged("Progress");
                }
            }
        }

        /// <summary>
        /// Result of Work
        /// </summary>
        public O Result
        {
            get { return _result; }
            set
            {
                if (!value.Equals(_result))
                {
                    _result = value;
                    OnPropertyChanged("Result");
                }
            }
        }

        public ICommand CancelCommand
        {
            get
            {
                if (_cancelCommand == null)
                {
                    _cancelCommand = new RelayCommand(_ =>
                    {
                        // If item is being processed, Call BackgroundWorker.CancelAsync()
                        // Else, set the status of the WorkItem to Cancelled
                        if (this != null)
                            CancelAsync();
                        else
                            Status = WorkStatus.Cancelled;
                    }, _ =>
                    {
                        // Returns true if WorkItem is being processed with a worker that supports cancellation
                        // or if the WorkItem is still Pending
                        // False if otherwise, eg. already completed, cancelled etc
                        if (Status == WorkStatus.Processing)
                        {
                            if (this != null && WorkerSupportsCancellation)
                                return true;
                        }
                        else if (Status == WorkStatus.Pending)
                            return true;

                        return false;
                    });
                }

                return _cancelCommand;
            }
        }

        #endregion

        #region Constructor

        public WorkItem(I args)
            : base()
        {
            _args = args;
            _status = WorkStatus.Pending;
            _progress = null;
            _result = default(O);
        }

        #endregion

        #region INotifyPropertyChanged implementation

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, e);
        }

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private void OnPropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null)
                throw new ArgumentNullException("PropertyNames cannot be null.");

            foreach (var propertyName in propertyNames)
                OnPropertyChanged(propertyName);
        }

        #endregion
    }
}
